package com.dustedpixels.jasmin.chips.z80.impl;

import com.dustedpixels.jasmin.chips.z80.Z80Context;
import com.dustedpixels.jasmin.chips.z80.implbase.Z80ImplBase;

/**
 * Native (non wrapping) Z80 implementation. 
 */
public final class Z80Version3 extends Z80ImplBase {
    private final Z80Context context;
    private long cycles;

    // Registers and flags.
    byte A, F, I, R;
    short BC, DE, HL, IX, IY, SP, PC;
    byte A_BIS, F_BIS;
    short BC_BIS, DE_BIS, HL_BIS;
    boolean IFF1, IFF2;
    byte IM;
    
    // Temporary field for use while exchanging;
    short tmp;

    public Z80Version3(Z80Context context) {
        this.context = context;
        this.cycles = Long.MIN_VALUE;
        
        PC((short) 0);
        R((byte) 0);
        IFF1(false); 
        IFF2(false);
        I((byte) 0);
        IM = 0;
    }
    
    public long getCycles() {
        return cycles;
    }
    
    public void processInstruction() {
        if (context.noLinesActive()) {
            processOpcode();
            
        } else {
            if (context.isResetActive()) {
                PC((short) 0);
                R((byte) 0);
                IFF1(false); 
                IFF2(false);
                I((byte) 0);
                IM = 0;
            } else if (context.isNMIActive()) {
                
            } else if (context.isIRQActive() & IFF1()) {
                incR();
                leaveHALT();
                IFF1(false);
                IFF2(false);
                if (IM == 0) {
                    //System.out.println("Inside IRQ, IM 0");
                    throw new RuntimeException("IM0 not implemented.");
                    //baseInstructionTable.fetch(irqValue).run();
                    //t(2);
                } else if (IM == 1) {
                    //System.out.println("Inside IRQ, IM 1");
                    rst((byte) 0x38);
                    t(13); // 11 (RST) + 2
                } else if (IM == 2) {
                    //System.out.println("Inside IRQ, IM 2");
                    short address = (short) (I() << 8);//irqValue;
                    //System.out.println(address + "->" + peek16(address));
                    call(peek16(address));
                    t(19);
                }
                return;
            }
        }
    }
    
    private void processOpcode() {
        incR();
        
        byte opcode = fetch();
        
        //System.out.println(Integer.toHexString(opcode) + ", HL: " + HL());

        switch (opcode) {
        case (byte) 0x00: t(4); break;
        case (byte) 0x01: BC(fetch16()); t(10); break;
        case (byte) 0x02: poke(BC(), A()); t(7); break;
        case (byte) 0x03: BC(inc0(BC())); t(6); break;
        case (byte) 0x04: B(inc(B())); t(4); break;
        case (byte) 0x05: B(dec(B())); t(4); break;
        case (byte) 0x06: B(fetch()); t(7); break;
        case (byte) 0x07: A(rlc(A(), true)); t(4); break;
        
        case (byte) 0x08: byte ta=A; byte tf=F; A=A_BIS; F=F_BIS; A_BIS=ta; F_BIS=tf; t(4); break;
        case (byte) 0x09: HL(add16(HL(), BC())); t(11); break;
        case (byte) 0x0A: A(peek(BC())); t(7); break;
        case (byte) 0x0B: BC(dec0(BC())); t(6); break;
        case (byte) 0x0C: C(inc(C())); t(4); break;
        case (byte) 0x0D: C(dec(C())); t(4); break;
        case (byte) 0x0E: C(fetch()); t(7); break;
        case (byte) 0x0F: A(rrc(A(), true)); t(4); break;

        case (byte) 0x10: if (DJNZ(fetch())) t(13); else t(8); break;
        case (byte) 0x11: DE(fetch16()); t(10); break;
        case (byte) 0x12: poke(DE(), A()); t(7); break;
        case (byte) 0x13: DE(inc0(DE())); t(6); break;
        case (byte) 0x14: D(inc(D())); t(4); break;
        case (byte) 0x15: D(dec(D())); t(4); break;
        case (byte) 0x16: D(fetch()); t(7); break;
        case (byte) 0x17: A(rl(A(), true)); t(4); break;
        
        case (byte) 0x18: jumpe(fetch()); t(12); break;
        case (byte) 0x19: HL(add16(HL(), DE())); t(11); break;
        case (byte) 0x1A: A(peek(DE())); t(7); break;
        case (byte) 0x1B: DE(dec0(DE())); t(6); break;
        case (byte) 0x1C: E(inc(E())); t(4); break;
        case (byte) 0x1D: E(dec(E())); t(4); break;
        case (byte) 0x1E: E(fetch()); t(7); break;
        case (byte) 0x1F: A(rr(A(), true)); t(4); break;
        
        case (byte) 0x20: if (jumpec(fetch(), !ZF())) t(12); else t(7); break;
        case (byte) 0x21: HL(fetch16()); t(10); break;
        case (byte) 0x22: poke16(fetch16(), HL()); t(16); break;
        case (byte) 0x23: HL(inc0(HL())); t(6); break;
        case (byte) 0x24: H(inc(H())); t(4); break;
        case (byte) 0x25: H(dec(H())); t(4); break;
        case (byte) 0x26: H(fetch()); t(7); break;
        case (byte) 0x27: DAA(); t(4); break;
        
        case (byte) 0x28: if (jumpec(fetch(), ZF())) t(12); else t(7); break;
        case (byte) 0x29: HL(add16(HL(), HL())); t(11); break;
        case (byte) 0x2A: HL(peek16(fetch16())); t(16); break;
        case (byte) 0x2B: HL(dec0(HL())); t(6); break;
        case (byte) 0x2C: L(inc(L())); t(4); break;
        case (byte) 0x2D: L(dec(L())); t(4); break;
        case (byte) 0x2E: L(fetch()); t(7); break;
        case (byte) 0x2F: CPL(); t(4); break;
        
        case (byte) 0x30: if (jumpec(fetch(), !CF())) t(12); else t(7); break;
        case (byte) 0x31: SP(fetch16()); t(10); break;
        case (byte) 0x32: poke(fetch16(), A()); t(13); break;
        case (byte) 0x33: SP(inc0(SP())); t(6); break;
        case (byte) 0x34: tmp=HL(); poke(tmp, inc(peek(tmp))); t(11); break;
        case (byte) 0x35: tmp=HL(); poke(tmp, dec(peek(tmp))); t(11); break;
        case (byte) 0x36: poke(HL(), fetch()); t(10); break;
        case (byte) 0x37: SCF(); t(4); break;
        
        case (byte) 0x38: if (jumpec(fetch(), CF())) t(12); else t(7); break;
        case (byte) 0x39: HL(add16(HL(), SP())); t(11); break;
        case (byte) 0x3A: A(peek(fetch16())); t(13); break;
        case (byte) 0x3B: SP(dec0(SP())); t(6); break;
        case (byte) 0x3C: A(inc(A())); t(4); break;
        case (byte) 0x3D: A(dec(A())); t(4); break;
        case (byte) 0x3E: A(fetch()); t(7); break;
        case (byte) 0x3F: CCF(); t(4); break; 
        
        case (byte) 0x40: B(B()); t(4); break;
        case (byte) 0x41: B(C()); t(4); break;
        case (byte) 0x42: B(D()); t(4); break;
        case (byte) 0x43: B(E()); t(4); break;
        case (byte) 0x44: B(H()); t(4); break;
        case (byte) 0x45: B(L()); t(4); break;
        case (byte) 0x46: B(peek(HL())); t(7); break;
        case (byte) 0x47: B(A()); t(4); break;

        case (byte) 0x48: C(B()); t(4); break;
        case (byte) 0x49: C(C()); t(4); break;
        case (byte) 0x4A: C(D()); t(4); break;
        case (byte) 0x4B: C(E()); t(4); break;
        case (byte) 0x4C: C(H()); t(4); break;
        case (byte) 0x4D: C(L()); t(4); break;
        case (byte) 0x4E: C(peek(HL())); t(7); break;
        case (byte) 0x4F: C(A()); t(4); break;

        case (byte) 0x50: D(B()); t(4); break;
        case (byte) 0x51: D(C()); t(4); break;
        case (byte) 0x52: D(D()); t(4); break;
        case (byte) 0x53: D(E()); t(4); break;
        case (byte) 0x54: D(H()); t(4); break;
        case (byte) 0x55: D(L()); t(4); break;
        case (byte) 0x56: D(peek(HL())); t(7); break;
        case (byte) 0x57: D(A()); t(4); break;

        case (byte) 0x58: E(B()); t(4); break;
        case (byte) 0x59: E(C()); t(4); break;
        case (byte) 0x5A: E(D()); t(4); break;
        case (byte) 0x5B: E(E()); t(4); break;
        case (byte) 0x5C: E(H()); t(4); break;
        case (byte) 0x5D: E(L()); t(4); break;
        case (byte) 0x5E: E(peek(HL())); t(7); break;
        case (byte) 0x5F: E(A()); t(4); break;

        case (byte) 0x60: H(B()); t(4); break;
        case (byte) 0x61: H(C()); t(4); break;
        case (byte) 0x62: H(D()); t(4); break;
        case (byte) 0x63: H(E()); t(4); break;
        case (byte) 0x64: H(H()); t(4); break;
        case (byte) 0x65: H(L()); t(4); break;
        case (byte) 0x66: H(peek(HL())); t(7); break;
        case (byte) 0x67: H(A()); t(4); break;

        case (byte) 0x68: L(B()); t(4); break;
        case (byte) 0x69: L(C()); t(4); break;
        case (byte) 0x6A: L(D()); t(4); break;
        case (byte) 0x6B: L(E()); t(4); break;
        case (byte) 0x6C: L(H()); t(4); break;
        case (byte) 0x6D: L(L()); t(4); break;
        case (byte) 0x6E: L(peek(HL())); t(7); break;
        case (byte) 0x6F: L(A()); t(4); break;

        case (byte) 0x70: poke(HL(), B()); t(7); break;
        case (byte) 0x71: poke(HL(), C()); t(7); break;
        case (byte) 0x72: poke(HL(), D()); t(7); break;
        case (byte) 0x73: poke(HL(), E()); t(7); break;
        case (byte) 0x74: poke(HL(), H()); t(7); break;
        case (byte) 0x75: poke(HL(), L()); t(7); break;
        case (byte) 0x76: jumpe((byte) -1); t(4); break;
        case (byte) 0x77: poke(HL(), A()); t(7); break;

        case (byte) 0x78: A(B()); t(4); break;
        case (byte) 0x79: A(C()); t(4); break;
        case (byte) 0x7A: A(D()); t(4); break;
        case (byte) 0x7B: A(E()); t(4); break;
        case (byte) 0x7C: A(H()); t(4); break;
        case (byte) 0x7D: A(L()); t(4); break;
        case (byte) 0x7E: A(peek(HL())); t(7); break;
        case (byte) 0x7F: A(A()); t(4); break;
        
        case (byte) 0x80: A(add(A(), B())); t(4); break;
        case (byte) 0x81: A(add(A(), C())); t(4); break;
        case (byte) 0x82: A(add(A(), D())); t(4); break;
        case (byte) 0x83: A(add(A(), E())); t(4); break;
        case (byte) 0x84: A(add(A(), H())); t(4); break;
        case (byte) 0x85: A(add(A(), L())); t(4); break;
        case (byte) 0x86: A(add(A(), peek(HL()))); t(7); break;
        case (byte) 0x87: A(add(A(), A())); t(4); break;
        
        case (byte) 0x88: A(adc(A(), B())); t(4); break;
        case (byte) 0x89: A(adc(A(), C())); t(4); break;
        case (byte) 0x8A: A(adc(A(), D())); t(4); break;
        case (byte) 0x8B: A(adc(A(), E())); t(4); break;
        case (byte) 0x8C: A(adc(A(), H())); t(4); break;
        case (byte) 0x8D: A(adc(A(), L())); t(4); break;
        case (byte) 0x8E: A(adc(A(), peek(HL()))); t(7); break;
        case (byte) 0x8F: A(adc(A(), A())); t(4); break;
        
        case (byte) 0x90: A(sub(A(), B())); t(4); break;
        case (byte) 0x91: A(sub(A(), C())); t(4); break;
        case (byte) 0x92: A(sub(A(), D())); t(4); break;
        case (byte) 0x93: A(sub(A(), E())); t(4); break;
        case (byte) 0x94: A(sub(A(), H())); t(4); break;
        case (byte) 0x95: A(sub(A(), L())); t(4); break;
        case (byte) 0x96: A(sub(A(), peek(HL()))); t(7); break;
        case (byte) 0x97: A(sub(A(), A())); t(4); break;
        
        case (byte) 0x98: A(sbc(A(), B())); t(4); break;
        case (byte) 0x99: A(sbc(A(), C())); t(4); break;
        case (byte) 0x9A: A(sbc(A(), D())); t(4); break;
        case (byte) 0x9B: A(sbc(A(), E())); t(4); break;
        case (byte) 0x9C: A(sbc(A(), H())); t(4); break;
        case (byte) 0x9D: A(sbc(A(), L())); t(4); break;
        case (byte) 0x9E: A(sbc(A(), peek(HL()))); t(7); break;
        case (byte) 0x9F: A(sbc(A(), A())); t(4); break;
        
        case (byte) 0xA0: A(and(A(), B())); t(4); break;
        case (byte) 0xA1: A(and(A(), C())); t(4); break;
        case (byte) 0xA2: A(and(A(), D())); t(4); break;
        case (byte) 0xA3: A(and(A(), E())); t(4); break;
        case (byte) 0xA4: A(and(A(), H())); t(4); break;
        case (byte) 0xA5: A(and(A(), L())); t(4); break;
        case (byte) 0xA6: A(and(A(), peek(HL()))); t(7); break;
        case (byte) 0xA7: A(and(A(), A())); t(4); break;
        
        case (byte) 0xA8: A(xor(A(), B())); t(4); break;
        case (byte) 0xA9: A(xor(A(), C())); t(4); break;
        case (byte) 0xAA: A(xor(A(), D())); t(4); break;
        case (byte) 0xAB: A(xor(A(), E())); t(4); break;
        case (byte) 0xAC: A(xor(A(), H())); t(4); break;
        case (byte) 0xAD: A(xor(A(), L())); t(4); break;
        case (byte) 0xAE: A(xor(A(), peek(HL()))); t(7); break;
        case (byte) 0xAF: A(xor(A(), A())); t(4); break;
        
        case (byte) 0xB0: A(or(A(), B())); t(4); break;
        case (byte) 0xB1: A(or(A(), C())); t(4); break;
        case (byte) 0xB2: A(or(A(), D())); t(4); break;
        case (byte) 0xB3: A(or(A(), E())); t(4); break;
        case (byte) 0xB4: A(or(A(), H())); t(4); break;
        case (byte) 0xB5: A(or(A(), L())); t(4); break;
        case (byte) 0xB6: A(or(A(), peek(HL()))); t(7); break;
        case (byte) 0xB7: A(or(A(), A())); t(4); break;
        
        case (byte) 0xB8: cp(A(), B()); t(4); break;
        case (byte) 0xB9: cp(A(), C()); t(4); break;
        case (byte) 0xBA: cp(A(), D()); t(4); break;
        case (byte) 0xBB: cp(A(), E()); t(4); break;
        case (byte) 0xBC: cp(A(), H()); t(4); break;
        case (byte) 0xBD: cp(A(), L()); t(4); break;
        case (byte) 0xBE: cp(A(), peek(HL())); t(7); break;
        case (byte) 0xBF: cp(A(), A()); t(4); break;
        
        case (byte) 0xC0: if (retc(!ZF())) t(11); else t(5); break;
        case (byte) 0xC1: BC(pop16()); t(10); break;
        case (byte) 0xC2: jumpc(fetch16(), !ZF()); t(10); break;
        case (byte) 0xC3: jump(fetch16()); t(10); break;
        case (byte) 0xC4: if (callc(fetch16(), !ZF())) t(17); else t(10); break;
        case (byte) 0xC5: push16(BC()); t(11); break;
        case (byte) 0xC6: A(add(A(), fetch())); t(7); break;
        case (byte) 0xC7: rst((byte) 0x00); t(11); break;
        
        case (byte) 0xC8: if (retc(ZF())) t(11); else t(5); break;
        case (byte) 0xC9: ret(); t(10); break;
        case (byte) 0xCA: jumpc(fetch16(), ZF()); t(10); break;
        case (byte) 0xCB: t(4); processCBOpcode(); break;
        case (byte) 0xCC: if (callc(fetch16(), ZF())) t(17); else t(10); break;
        case (byte) 0xCD: call(fetch16()); t(17); break;
        case (byte) 0xCE: A(adc(A(), fetch())); t(7); break;
        case (byte) 0xCF: rst((byte) 0x08); t(11); break;
        
        case (byte) 0xD0: if (retc(!CF())) t(11); else t(5); break;
        case (byte) 0xD1: DE(pop16()); t(10); break;
        case (byte) 0xD2: jumpc(fetch16(), !CF()); t(10); break;
        case (byte) 0xD3: OUTA(fetch()); t(11); break;
        case (byte) 0xD4: if (callc(fetch16(), !CF())) t(17); else t(10); break;
        case (byte) 0xD5: push16(DE()); t(11); break;
        case (byte) 0xD6: A(sub(A(), fetch())); t(7); break;
        case (byte) 0xD7: rst((byte) 0x10); t(11); break;

        case (byte) 0xD8: if (retc(CF())) t(11); else t(5); break;
        case (byte) 0xD9: EXX(); t(4); break;
        case (byte) 0xDA: jumpc(fetch16(), CF()); t(10); break;
        case (byte) 0xDB: INA(fetch()); t(11); break;
        case (byte) 0xDC: if (callc(fetch16(), CF())) t(17); else t(10); break;
        case (byte) 0xDD: t(4); processDDOpcode(); break;
        case (byte) 0xDE: A(sbc(A(), fetch())); t(7); break;
        case (byte) 0xDF: rst((byte) 0x18); t(11); break;

        case (byte) 0xE0: if (retc(!PF())) t(11); else t(5); break;
        case (byte) 0xE1: HL(pop16()); t(10); break;
        case (byte) 0xE2: jumpc(fetch16(), !PF()); t(10); break;
        case (byte) 0xE3: tmp=peek16(SP()); poke16(SP(), HL()); HL(tmp); t(19); break;
        case (byte) 0xE4: if (callc(fetch16(), !PF())) t(17); else t(10); break;
        case (byte) 0xE5: push16(HL()); t(11); break;
        case (byte) 0xE6: A(and(A(), fetch())); t(7); break;
        case (byte) 0xE7: rst((byte)0x20); t(11); break;
        
        case (byte) 0xE8: if (retc(PF())) t(11); else t(5); break;
        case (byte) 0xE9: jump(HL()); t(4); break;
        case (byte) 0xEA: jumpc(fetch16(), PF()); t(10); break;
        case (byte) 0xEB: tmp = DE(); DE(HL()); HL(tmp); t(4); break;
        case (byte) 0xEC: if (callc(fetch16(), PF())) t(17); else t(10); break;
        case (byte) 0xED: t(4); processEDOpcode(); break;
        case (byte) 0xEE: A(xor(A(), fetch())); t(7); break;
        case (byte) 0xEF: rst((byte) 0x28); t(11); break;
        
        case (byte) 0xF0: if (retc(!SF())) t(11); else t(5); break;
        case (byte) 0xF1: AF(pop16()); t(10); break;
        case (byte) 0xF2: jumpc(fetch16(), !SF()); t(10); break;
        case (byte) 0xF3: DI(); t(4); break;
        case (byte) 0xF4: if (callc(fetch16(), !SF())) t(17); else t(10); break;
        case (byte) 0xF5: push16(AF()); t(11); break;
        case (byte) 0xF6: A(or(A(), fetch())); t(7); break;
        case (byte) 0xF7: rst((byte) 0x30); t(11); break;

        case (byte) 0xF8: if (retc(SF())) t(11); else t(5); break;
        case (byte) 0xF9: SP(HL()); t(6); break;
        case (byte) 0xFA: jumpc(fetch16(), SF()); t(10); break;
        case (byte) 0xFB: EI(); t(4); break;
        case (byte) 0xFC: if (callc(fetch16(), SF())) t(17); else t(10); break;
        case (byte) 0xFD: t(4); processFDOpcode(); break;
        case (byte) 0xFE: cp(A(), fetch()); t(7); break;
        case (byte) 0xFF: rst((byte) 0x38); t(11); break;
        }
    }
    
    private void processCBOpcode() {
        incR();
        
        byte opcode = fetch();

        switch (opcode) {
        case (byte) 0x00: B(rlc(B(), false)); t(4); break;
        case (byte) 0x01: C(rlc(C(), false)); t(4); break;
        case (byte) 0x02: D(rlc(D(), false)); t(4); break;
        case (byte) 0x03: E(rlc(E(), false)); t(4); break;
        case (byte) 0x04: H(rlc(H(), false)); t(4); break;
        case (byte) 0x05: L(rlc(L(), false)); t(4); break;
        case (byte) 0x06: tmp=HL(); poke(HL, rlc(peek(HL), false)); t(11); break;
        case (byte) 0x07: A(rlc(A(), false)); t(4); break;

        case (byte) 0x08: B(rrc(B(), false)); t(4); break;
        case (byte) 0x09: C(rrc(C(), false)); t(4); break;
        case (byte) 0x0A: D(rrc(D(), false)); t(4); break;
        case (byte) 0x0B: E(rrc(E(), false)); t(4); break;
        case (byte) 0x0C: H(rrc(H(), false)); t(4); break;
        case (byte) 0x0D: L(rrc(L(), false)); t(4); break;
        case (byte) 0x0E: tmp=HL(); poke(HL, rrc(peek(HL), false)); t(11); break;
        case (byte) 0x0F: A(rrc(A(), false)); t(4); break;
        
        case (byte) 0x10: B(rl(B(), false)); t(4); break;
        case (byte) 0x11: C(rl(C(), false)); t(4); break;
        case (byte) 0x12: D(rl(D(), false)); t(4); break;
        case (byte) 0x13: E(rl(E(), false)); t(4); break;
        case (byte) 0x14: H(rl(H(), false)); t(4); break;
        case (byte) 0x15: L(rl(L(), false)); t(4); break;
        case (byte) 0x16: tmp=HL(); poke(HL, rl(peek(HL), false)); t(11); break;
        case (byte) 0x17: A(rl(A(), false)); t(4); break;
        
        case (byte) 0x18: B(rr(B(), false)); t(4); break;
        case (byte) 0x19: C(rr(C(), false)); t(4); break;
        case (byte) 0x1A: D(rr(D(), false)); t(4); break;
        case (byte) 0x1B: E(rr(E(), false)); t(4); break;
        case (byte) 0x1C: H(rr(H(), false)); t(4); break;
        case (byte) 0x1D: L(rr(L(), false)); t(4); break;
        case (byte) 0x1E: tmp=HL(); poke(HL, rr(peek(HL), false)); t(11); break;
        case (byte) 0x1F: A(rr(A(), false)); t(4); break;
    
        case (byte) 0x20: B(sla(B())); t(4); break;
        case (byte) 0x21: C(sla(C())); t(4); break;
        case (byte) 0x22: D(sla(D())); t(4); break;
        case (byte) 0x23: E(sla(E())); t(4); break;
        case (byte) 0x24: H(sla(H())); t(4); break;
        case (byte) 0x25: L(sla(L())); t(4); break;
        case (byte) 0x26: tmp=HL(); poke(HL, sla(peek(HL))); t(11); break;
        case (byte) 0x27: A(sla(A())); t(4); break;
        
        case (byte) 0x28: B(sra(B())); t(4); break;
        case (byte) 0x29: C(sra(C())); t(4); break;
        case (byte) 0x2A: D(sra(D())); t(4); break;
        case (byte) 0x2B: E(sra(E())); t(4); break;
        case (byte) 0x2C: H(sra(H())); t(4); break;
        case (byte) 0x2D: L(sra(L())); t(4); break;
        case (byte) 0x2E: tmp=HL(); poke(HL, sra(peek(HL))); t(11); break;
        case (byte) 0x2F: A(sra(A())); t(4); break;
        
        case (byte) 0x30: B(sll(B())); t(4); break;
        case (byte) 0x31: C(sll(C())); t(4); break;
        case (byte) 0x32: D(sll(D())); t(4); break;
        case (byte) 0x33: E(sll(E())); t(4); break;
        case (byte) 0x34: H(sll(H())); t(4); break;
        case (byte) 0x35: L(sll(L())); t(4); break;
        case (byte) 0x36: tmp=HL(); poke(HL, sll(peek(HL))); t(11); break;
        case (byte) 0x37: A(sll(A())); t(4); break;

        case (byte) 0x38: B(srl(B())); t(4); break;
        case (byte) 0x39: C(srl(C())); t(4); break;
        case (byte) 0x3A: D(srl(D())); t(4); break;
        case (byte) 0x3B: E(srl(E())); t(4); break;
        case (byte) 0x3C: H(srl(H())); t(4); break;
        case (byte) 0x3D: L(srl(L())); t(4); break;
        case (byte) 0x3E: tmp=HL(); poke(HL, srl(peek(HL))); t(11); break;
        case (byte) 0x3F: A(srl(A())); t(4); break;
        
        case (byte) 0x40: bit(0, B()); t(4); break;
        case (byte) 0x41: bit(0, C()); t(4); break;
        case (byte) 0x42: bit(0, D()); t(4); break;
        case (byte) 0x43: bit(0, E()); t(4); break;
        case (byte) 0x44: bit(0, H()); t(4); break;
        case (byte) 0x45: bit(0, L()); t(4); break;
        case (byte) 0x46: biti(0, HL()); t(8); break;
        case (byte) 0x47: bit(0, A()); t(4); break;
        
        case (byte) 0x48: bit(1, B()); t(4); break;
        case (byte) 0x49: bit(1, C()); t(4); break;
        case (byte) 0x4A: bit(1, D()); t(4); break;
        case (byte) 0x4B: bit(1, E()); t(4); break;
        case (byte) 0x4C: bit(1, H()); t(4); break;
        case (byte) 0x4D: bit(1, L()); t(4); break;
        case (byte) 0x4E: biti(1, HL()); t(8); break;
        case (byte) 0x4F: bit(1, A()); t(4); break;
        
        case (byte) 0x50: bit(2, B()); t(4); break;
        case (byte) 0x51: bit(2, C()); t(4); break;
        case (byte) 0x52: bit(2, D()); t(4); break;
        case (byte) 0x53: bit(2, E()); t(4); break;
        case (byte) 0x54: bit(2, H()); t(4); break;
        case (byte) 0x55: bit(2, L()); t(4); break;
        case (byte) 0x56: biti(2, HL()); t(8); break;
        case (byte) 0x57: bit(2, A()); t(4); break;
        
        case (byte) 0x58: bit(3, B()); t(4); break;
        case (byte) 0x59: bit(3, C()); t(4); break;
        case (byte) 0x5A: bit(3, D()); t(4); break;
        case (byte) 0x5B: bit(3, E()); t(4); break;
        case (byte) 0x5C: bit(3, H()); t(4); break;
        case (byte) 0x5D: bit(3, L()); t(4); break;
        case (byte) 0x5E: biti(3, HL()); t(8); break;
        case (byte) 0x5F: bit(3, A()); t(4); break;
    
        case (byte) 0x60: bit(4, B()); t(4); break;
        case (byte) 0x61: bit(4, C()); t(4); break;
        case (byte) 0x62: bit(4, D()); t(4); break;
        case (byte) 0x63: bit(4, E()); t(4); break;
        case (byte) 0x64: bit(4, H()); t(4); break;
        case (byte) 0x65: bit(4, L()); t(4); break;
        case (byte) 0x66: biti(4, HL()); t(8); break;
        case (byte) 0x67: bit(4, A()); t(4); break;
        
        case (byte) 0x68: bit(5, B()); t(4); break;
        case (byte) 0x69: bit(5, C()); t(4); break;
        case (byte) 0x6A: bit(5, D()); t(4); break;
        case (byte) 0x6B: bit(5, E()); t(4); break;
        case (byte) 0x6C: bit(5, H()); t(4); break;
        case (byte) 0x6D: bit(5, L()); t(4); break;
        case (byte) 0x6E: biti(5, HL()); t(8); break;
        case (byte) 0x6F: bit(5, A()); t(4); break;
    
        case (byte) 0x70: bit(6, B()); t(4); break;
        case (byte) 0x71: bit(6, C()); t(4); break;
        case (byte) 0x72: bit(6, D()); t(4); break;
        case (byte) 0x73: bit(6, E()); t(4); break;
        case (byte) 0x74: bit(6, H()); t(4); break;
        case (byte) 0x75: bit(6, L()); t(4); break;
        case (byte) 0x76: biti(6, HL()); t(8); break;
        case (byte) 0x77: bit(6, A()); t(4); break;
        
        case (byte) 0x78: bit(7, B()); t(4); break;
        case (byte) 0x79: bit(7, C()); t(4); break;
        case (byte) 0x7A: bit(7, D()); t(4); break;
        case (byte) 0x7B: bit(7, E()); t(4); break;
        case (byte) 0x7C: bit(7, H()); t(4); break;
        case (byte) 0x7D: bit(7, L()); t(4); break;
        case (byte) 0x7E: biti(7, HL()); t(8); break;
        case (byte) 0x7F: bit(7, A()); t(4); break;
        
        case (byte) 0x80: B(res(0, B())); t(4); break;
        case (byte) 0x81: C(res(0, C())); t(4); break;
        case (byte) 0x82: D(res(0, D())); t(4); break;
        case (byte) 0x83: E(res(0, E())); t(4); break;
        case (byte) 0x84: H(res(0, H())); t(4); break;
        case (byte) 0x85: L(res(0, L())); t(4); break;
        case (byte) 0x86: tmp=HL(); poke(tmp, res(0, peek(tmp))); t(11); break;
        case (byte) 0x87: A(res(0, A())); t(4); break;
        
        case (byte) 0x88: B(res(1, B())); t(4); break;
        case (byte) 0x89: C(res(1, C())); t(4); break;
        case (byte) 0x8A: D(res(1, D())); t(4); break;
        case (byte) 0x8B: E(res(1, E())); t(4); break;
        case (byte) 0x8C: H(res(1, H())); t(4); break;
        case (byte) 0x8D: L(res(1, L())); t(4); break;
        case (byte) 0x8E: tmp=HL(); poke(tmp, res(1, peek(tmp))); t(11); break;
        case (byte) 0x8F: A(res(1, A())); t(4); break;
        
        case (byte) 0x90: B(res(2, B())); t(4); break;
        case (byte) 0x91: C(res(2, C())); t(4); break;
        case (byte) 0x92: D(res(2, D())); t(4); break;
        case (byte) 0x93: E(res(2, E())); t(4); break;
        case (byte) 0x94: H(res(2, H())); t(4); break;
        case (byte) 0x95: L(res(2, L())); t(4); break;
        case (byte) 0x96: tmp=HL(); poke(tmp, res(2, peek(tmp))); t(11); break;
        case (byte) 0x97: A(res(2, A())); t(4); break;
        
        case (byte) 0x98: B(res(3, B())); t(4); break;
        case (byte) 0x99: C(res(3, C())); t(4); break;
        case (byte) 0x9A: D(res(3, D())); t(4); break;
        case (byte) 0x9B: E(res(3, E())); t(4); break;
        case (byte) 0x9C: H(res(3, H())); t(4); break;
        case (byte) 0x9D: L(res(3, L())); t(4); break;
        case (byte) 0x9E: tmp=HL(); poke(tmp, res(3, peek(tmp))); t(11); break;
        case (byte) 0x9F: A(res(3, A())); t(4); break;
        
        case (byte) 0xA0: B(res(4, B())); t(4); break;
        case (byte) 0xA1: C(res(4, C())); t(4); break;
        case (byte) 0xA2: D(res(4, D())); t(4); break;
        case (byte) 0xA3: E(res(4, E())); t(4); break;
        case (byte) 0xA4: H(res(4, H())); t(4); break;
        case (byte) 0xA5: L(res(4, L())); t(4); break;
        case (byte) 0xA6: tmp=HL(); poke(tmp, res(4, peek(tmp))); t(11); break;
        case (byte) 0xA7: A(res(4, A())); t(4); break;
        
        case (byte) 0xA8: B(res(5, B())); t(4); break;
        case (byte) 0xA9: C(res(5, C())); t(4); break;
        case (byte) 0xAA: D(res(5, D())); t(4); break;
        case (byte) 0xAB: E(res(5, E())); t(4); break;
        case (byte) 0xAC: H(res(5, H())); t(4); break;
        case (byte) 0xAD: L(res(5, L())); t(4); break;
        case (byte) 0xAE: tmp=HL(); poke(tmp, res(5, peek(tmp))); t(11); break;
        case (byte) 0xAF: A(res(5, A())); t(4); break;
        
        case (byte) 0xB0: B(res(6, B())); t(4); break;
        case (byte) 0xB1: C(res(6, C())); t(4); break;
        case (byte) 0xB2: D(res(6, D())); t(4); break;
        case (byte) 0xB3: E(res(6, E())); t(4); break;
        case (byte) 0xB4: H(res(6, H())); t(4); break;
        case (byte) 0xB5: L(res(6, L())); t(4); break;
        case (byte) 0xB6: tmp=HL(); poke(tmp, res(6, peek(tmp))); t(11); break;
        case (byte) 0xB7: A(res(6, A())); t(4); break;
        
        case (byte) 0xB8: B(res(7, B())); t(4); break;
        case (byte) 0xB9: C(res(7, C())); t(4); break;
        case (byte) 0xBA: D(res(7, D())); t(4); break;
        case (byte) 0xBB: E(res(7, E())); t(4); break;
        case (byte) 0xBC: H(res(7, H())); t(4); break;
        case (byte) 0xBD: L(res(7, L())); t(4); break;
        case (byte) 0xBE: tmp=HL(); poke(tmp, res(7, peek(tmp))); t(11); break;
        case (byte) 0xBF: A(res(7, A())); t(4); break;

        case (byte) 0xC0: B(set(0, B())); t(4); break;
        case (byte) 0xC1: C(set(0, C())); t(4); break;
        case (byte) 0xC2: D(set(0, D())); t(4); break;
        case (byte) 0xC3: E(set(0, E())); t(4); break;
        case (byte) 0xC4: H(set(0, H())); t(4); break;
        case (byte) 0xC5: L(set(0, L())); t(4); break;
        case (byte) 0xC6: tmp=HL(); poke(tmp, set(0, peek(tmp))); t(11); break;
        case (byte) 0xC7: A(set(0, A())); t(4); break;
        
        case (byte) 0xC8: B(set(1, B())); t(4); break;
        case (byte) 0xC9: C(set(1, C())); t(4); break;
        case (byte) 0xCA: D(set(1, D())); t(4); break;
        case (byte) 0xCB: E(set(1, E())); t(4); break;
        case (byte) 0xCC: H(set(1, H())); t(4); break;
        case (byte) 0xCD: L(set(1, L())); t(4); break;
        case (byte) 0xCE: tmp=HL(); poke(tmp, set(1, peek(tmp))); t(11); break;
        case (byte) 0xCF: A(set(1, A())); t(4); break;
        
        case (byte) 0xD0: B(set(2, B())); t(4); break;
        case (byte) 0xD1: C(set(2, C())); t(4); break;
        case (byte) 0xD2: D(set(2, D())); t(4); break;
        case (byte) 0xD3: E(set(2, E())); t(4); break;
        case (byte) 0xD4: H(set(2, H())); t(4); break;
        case (byte) 0xD5: L(set(2, L())); t(4); break;
        case (byte) 0xD6: tmp=HL(); poke(tmp, set(2, peek(tmp))); t(11); break;
        case (byte) 0xD7: A(set(2, A())); t(4); break;
        
        case (byte) 0xD8: B(set(3, B())); t(4); break;
        case (byte) 0xD9: C(set(3, C())); t(4); break;
        case (byte) 0xDA: D(set(3, D())); t(4); break;
        case (byte) 0xDB: E(set(3, E())); t(4); break;
        case (byte) 0xDC: H(set(3, H())); t(4); break;
        case (byte) 0xDD: L(set(3, L())); t(4); break;
        case (byte) 0xDE: tmp=HL(); poke(tmp, set(3, peek(tmp))); t(11); break;
        case (byte) 0xDF: A(set(3, A())); t(4); break;
        
        case (byte) 0xE0: B(set(4, B())); t(4); break;
        case (byte) 0xE1: C(set(4, C())); t(4); break;
        case (byte) 0xE2: D(set(4, D())); t(4); break;
        case (byte) 0xE3: E(set(4, E())); t(4); break;
        case (byte) 0xE4: H(set(4, H())); t(4); break;
        case (byte) 0xE5: L(set(4, L())); t(4); break;
        case (byte) 0xE6: tmp=HL(); poke(tmp, set(4, peek(tmp))); t(11); break;
        case (byte) 0xE7: A(set(4, A())); t(4); break;
        
        case (byte) 0xE8: B(set(5, B())); t(4); break;
        case (byte) 0xE9: C(set(5, C())); t(4); break;
        case (byte) 0xEA: D(set(5, D())); t(4); break;
        case (byte) 0xEB: E(set(5, E())); t(4); break;
        case (byte) 0xEC: H(set(5, H())); t(4); break;
        case (byte) 0xED: L(set(5, L())); t(4); break;
        case (byte) 0xEE: tmp=HL(); poke(tmp, set(5, peek(tmp))); t(11); break;
        case (byte) 0xEF: A(set(5, A())); t(4); break;
        
        case (byte) 0xF0: B(set(6, B())); t(4); break;
        case (byte) 0xF1: C(set(6, C())); t(4); break;
        case (byte) 0xF2: D(set(6, D())); t(4); break;
        case (byte) 0xF3: E(set(6, E())); t(4); break;
        case (byte) 0xF4: H(set(6, H())); t(4); break;
        case (byte) 0xF5: L(set(6, L())); t(4); break;
        case (byte) 0xF6: tmp=HL(); poke(tmp, set(6, peek(tmp))); t(11); break;
        case (byte) 0xF7: A(set(6, A())); t(4); break;
        
        case (byte) 0xF8: B(set(7, B())); t(4); break;
        case (byte) 0xF9: C(set(7, C())); t(4); break;
        case (byte) 0xFA: D(set(7, D())); t(4); break;
        case (byte) 0xFB: E(set(7, E())); t(4); break;
        case (byte) 0xFC: H(set(7, H())); t(4); break;
        case (byte) 0xFD: L(set(7, L())); t(4); break;
        case (byte) 0xFE: tmp=HL(); poke(tmp, set(7, peek(tmp))); t(11); break;
        case (byte) 0xFF: A(set(7, A())); t(4); break;
        default: // System.err.println("CB Opcode unknown: " + Integer.toHexString(opcode));
        }
    }
    
    private void processDDOpcode() {
        incR();

        byte opcode = fetch();

        switch (opcode) {
        case (byte) 0x09: IX(add16(IX(), BC())); t(11); break;

        case (byte) 0x19: IX(add16(IX(), DE())); t(11); break;
        case (byte) 0x21: IX(fetch16()); t(10); break;
        case (byte) 0x22: poke16(fetch16(), IX()); t(16); break;
        case (byte) 0x23: IX(inc0(IX())); t(6); break;
        case (byte) 0x24: IXh(inc(IXh())); t(4); break;
        case (byte) 0x25: IXh(dec(IXh())); t(4); break;
        case (byte) 0x26: IXh(fetch()); t(7); break;

        case (byte) 0x29: IX(add16(IX(), IX())); t(11); break;
        case (byte) 0x2A: IX(peek16(fetch16())); t(16); break;
        case (byte) 0x2B: IX(dec0(IX())); t(6); break;
        case (byte) 0x2C: IXl(inc(IXl())); t(4); break;
        case (byte) 0x2D: IXl(dec(IXl())); t(4); break;
        case (byte) 0x2E: IXl(fetch()); t(7); break;

        case (byte) 0x34: tmp=add0(IX(), fetch()); poke(tmp, inc(peek(tmp))); t(19); break;
        case (byte) 0x35: tmp=add0(IX(), fetch()); poke(tmp, dec(peek(tmp))); t(19); break;
        case (byte) 0x36: poke(add0(IX(), fetch()), fetch()); t(15); break;
        case (byte) 0x39: IX(add16(IX(), SP())); t(11); break;
        
        case (byte) 0x44: B(IXh()); t(4); break;
        case (byte) 0x45: B(IXl()); t(4); break;
        case (byte) 0x46: B(peek(add0(IX(), fetch()))); t(15); break;
        
        case (byte) 0x4C: C(IXh()); t(4); break;
        case (byte) 0x4D: C(IXl()); t(4); break;
        case (byte) 0x4E: C(peek(add0(IX(), fetch()))); t(15); break;
        
        case (byte) 0x54: D(IXh()); t(4); break;
        case (byte) 0x55: D(IXl()); t(4); break;
        case (byte) 0x56: D(peek(add0(IX(), fetch()))); t(15); break;

        case (byte) 0x5C: E(IXh()); t(4); break;
        case (byte) 0x5D: E(IXl()); t(4); break;
        case (byte) 0x5E: E(peek(add0(IX(), fetch()))); t(15); break;
        
        case (byte) 0x60: IXh(B()); t(4); break;
        case (byte) 0x61: IXh(C()); t(4); break;
        case (byte) 0x62: IXh(D()); t(4); break;
        case (byte) 0x63: IXh(E()); t(4); break;
        case (byte) 0x64: IXh(IXh()); t(4); break;
        case (byte) 0x65: IXh(IXl()); t(4); break;
        case (byte) 0x66: H(peek(add0(IX(), fetch()))); t(15); break;
        case (byte) 0x67: IXh(A()); t(4); break;

        case (byte) 0x68: IXl(B()); t(4); break;
        case (byte) 0x69: IXl(C()); t(4); break;
        case (byte) 0x6A: IXl(D()); t(4); break;
        case (byte) 0x6B: IXl(E()); t(4); break;
        case (byte) 0x6C: IXl(IXh()); t(4); break;
        case (byte) 0x6D: IXl(IXl()); t(4); break;
        case (byte) 0x6E: L(peek(add0(IX(), fetch()))); t(15); break;
        case (byte) 0x6F: IXl(A()); t(4); break;
        
        case (byte) 0x70: poke(add0(IX(), fetch()), B()); t(15); break;
        case (byte) 0x71: poke(add0(IX(), fetch()), C()); t(15); break;
        case (byte) 0x72: poke(add0(IX(), fetch()), D()); t(15); break;
        case (byte) 0x73: poke(add0(IX(), fetch()), E()); t(15); break;
        case (byte) 0x74: poke(add0(IX(), fetch()), H()); t(15); break;
        case (byte) 0x75: poke(add0(IX(), fetch()), L()); t(15); break;
        case (byte) 0x77: poke(add0(IX(), fetch()), A()); t(15); break;

        case (byte) 0x7C: A(IXh()); t(4); break;
        case (byte) 0x7D: A(IXl()); t(4); break;
        case (byte) 0x7E: A(peek(add0(IX(), fetch()))); t(15); break;
        
        case (byte) 0x84: A(add(A(), IXh())); t(4); break; 
        case (byte) 0x85: A(add(A(), IXl())); t(4); break; 
        case (byte) 0x86: A(add(A(), peek(add0(IX(), fetch())))); t(15); break;

        case (byte) 0x8C: A(adc(A(), IXh())); t(4); break; 
        case (byte) 0x8D: A(adc(A(), IXl())); t(4); break; 
        case (byte) 0x8E: A(adc(A(), peek(add0(IX(), fetch())))); t(15); break;
        
        case (byte) 0x94: A(sub(A(), IXh())); t(4); break; 
        case (byte) 0x95: A(sub(A(), IXl())); t(4); break; 
        case (byte) 0x96: A(sub(A(), peek(add0(IX(), fetch())))); t(15); break;

        case (byte) 0x9C: A(sbc(A(), IXh())); t(4); break; 
        case (byte) 0x9D: A(sbc(A(), IXl())); t(4); break; 
        case (byte) 0x9E: A(sbc(A(), peek(add0(IX(), fetch())))); t(15); break;
        
        case (byte) 0xA4: A(and(A(), IXh())); t(4); break; 
        case (byte) 0xA5: A(and(A(), IXl())); t(4); break; 
        case (byte) 0xA6: A(and(A(), peek(add0(IX(), fetch())))); t(15); break;
        
        case (byte) 0xAC: A(xor(A(), IXh())); t(4); break; 
        case (byte) 0xAD: A(xor(A(), IXl())); t(4); break; 
        case (byte) 0xAE: A(xor(A(), peek(add0(IX(), fetch())))); t(15); break;
        
        case (byte) 0xB4: A(or(A(), IXh())); t(4); break; 
        case (byte) 0xB5: A(or(A(), IXl())); t(4); break; 
        case (byte) 0xB6: A(or(A(), peek(add0(IX(), fetch())))); t(15); break;
        
        case (byte) 0xBC: cp(A(), IXh()); t(4); break;
        case (byte) 0xBD: cp(A(), IXl()); t(4); break;
        case (byte) 0xBE: cp(A(), peek(add0(IX(), fetch()))); t(15); break;
        
        case (byte) 0xCB: t(4); processDDCBOpcode(); break;
        
        case (byte) 0xE1: IX(pop16()); t(11); break;
        case (byte) 0xE3: tmp=peek16(SP()); poke16(SP(), IX()); IX(tmp); t(19); break;
        case (byte) 0xE5: push16(IX()); t(11); break;
        
        case (byte) 0xE9: jump(IX()); t(4); break;

        case (byte) 0xF9: SP(IX()); t(6); break;
        default: // System.err.println("DD Opcode unknown: " + Integer.toHexString(opcode));
        }
    }
    
    private void processDDCBOpcode() {
        byte d = fetch();

        byte opcode = fetch();

        switch (opcode) {
        case (byte) 0x06: tmp=add0(IX(), d); poke(tmp, rlc(peek(tmp), false)); t(15); break;
        case (byte) 0x0E: tmp=add0(IX(), d); poke(tmp, rrc(peek(tmp), false)); t(15); break;
        case (byte) 0x16: tmp=add0(IX(), d); poke(tmp, rl(peek(tmp), false)); t(15); break;
        case (byte) 0x1E: tmp=add0(IX(), d); poke(tmp, rr(peek(tmp), false)); t(15); break;
        case (byte) 0x26: tmp=add0(IX(), d); poke(tmp, sla(peek(tmp))); t(15); break;
        case (byte) 0x2E: tmp=add0(IX(), d); poke(tmp, sra(peek(tmp))); t(15); break;
        case (byte) 0x36: tmp=add0(IX(), d); poke(tmp, sll(peek(tmp))); t(15); break;
        case (byte) 0x3E: tmp=add0(IX(), d); poke(tmp, srl(peek(tmp))); t(15); break;
        
        case (byte) 0x46: tmp=add0(IX(), d); biti(0, tmp); t(12); break;
        case (byte) 0x4E: tmp=add0(IX(), d); biti(1, tmp); t(12); break;
        case (byte) 0x56: tmp=add0(IX(), d); biti(2, tmp); t(12); break;
        case (byte) 0x5E: tmp=add0(IX(), d); biti(3, tmp); t(12); break;
        case (byte) 0x66: tmp=add0(IX(), d); biti(4, tmp); t(12); break;
        case (byte) 0x6E: tmp=add0(IX(), d); biti(5, tmp); t(12); break;
        case (byte) 0x76: tmp=add0(IX(), d); biti(6, tmp); t(12); break;
        case (byte) 0x7E: tmp=add0(IX(), d); biti(7, tmp); t(12); break;
        
        case (byte) 0x86: tmp=add0(IX(), d); poke(tmp, res(0, peek(tmp))); t(15); break;
        case (byte) 0x8E: tmp=add0(IX(), d); poke(tmp, res(1, peek(tmp))); t(15); break;
        case (byte) 0x96: tmp=add0(IX(), d); poke(tmp, res(2, peek(tmp))); t(15); break;
        case (byte) 0x9E: tmp=add0(IX(), d); poke(tmp, res(3, peek(tmp))); t(15); break;
        case (byte) 0xA6: tmp=add0(IX(), d); poke(tmp, res(4, peek(tmp))); t(15); break;
        case (byte) 0xAE: tmp=add0(IX(), d); poke(tmp, res(5, peek(tmp))); t(15); break;
        case (byte) 0xB6: tmp=add0(IX(), d); poke(tmp, res(6, peek(tmp))); t(15); break;
        case (byte) 0xBE: tmp=add0(IX(), d); poke(tmp, res(7, peek(tmp))); t(15); break;

        case (byte) 0xC6: tmp=add0(IX(), d); poke(tmp, set(0, peek(tmp))); t(15); break;
        case (byte) 0xCE: tmp=add0(IX(), d); poke(tmp, set(1, peek(tmp))); t(15); break;
        case (byte) 0xD6: tmp=add0(IX(), d); poke(tmp, set(2, peek(tmp))); t(15); break;
        case (byte) 0xDE: tmp=add0(IX(), d); poke(tmp, set(3, peek(tmp))); t(15); break;
        case (byte) 0xE6: tmp=add0(IX(), d); poke(tmp, set(4, peek(tmp))); t(15); break;
        case (byte) 0xEE: tmp=add0(IX(), d); poke(tmp, set(5, peek(tmp))); t(15); break;
        case (byte) 0xF6: tmp=add0(IX(), d); poke(tmp, set(6, peek(tmp))); t(15); break;
        case (byte) 0xFE: tmp=add0(IX(), d); poke(tmp, set(7, peek(tmp))); t(15); break;
        default: // System.err.println("DDCB Opcode unknown: " + Integer.toHexString(opcode));
        }
    }
    
    private void processEDOpcode() {
        incR();

        byte opcode = fetch();

        switch (opcode) {
        case (byte) 0x40: B(IN()); t(8); break;
        case (byte) 0x41: OUT(B()); t(8); break;
        case (byte) 0x42: HL(sbc16(HL(), BC())); t(11); break;
        case (byte) 0x43: poke16(fetch16(), BC()); t(16); break;
        case (byte) 0x44: NEG(); t(4); break;
        case (byte) 0x45: retn(); t(10); break;
        case (byte) 0x46: IM=0; t(4); break;
        case (byte) 0x47: I(A()); t(5); break;
        
        case (byte) 0x48: C(IN()); t(8); break;
        case (byte) 0x49: OUT(C()); t(8); break;
        case (byte) 0x4A: HL(adc16(HL(), BC())); t(11); break;
        case (byte) 0x4B: BC(peek16(fetch16())); t(16); break;
        case (byte) 0x4D: reti(); t(6); break;
        
        case (byte) 0x4F: R(A()); t(5); break;

        case (byte) 0x50: D(IN()); t(8); break;
        case (byte) 0x51: OUT(D()); t(8); break;
        case (byte) 0x52: HL(sbc16(HL(), DE())); t(11); break;
        case (byte) 0x53: poke16(fetch16(), DE()); t(16); break;
        case (byte) 0x56: IM=1; t(4); break;

        case (byte) 0x58: E(IN()); t(8); break;
        case (byte) 0x59: OUT(E()); t(8); break;
        case (byte) 0x5A: HL(adc16(HL(), DE())); t(11); break;
        case (byte) 0x5B: DE(peek16(fetch16())); t(16); break;

        case (byte) 0x57: LD_A_I(); t(5); break;
        case (byte) 0x5E: IM=2; t(4); break;
        case (byte) 0x5F: LD_A_R(); t(5); break;
        
        case (byte) 0x60: H(IN()); t(8); break;
        case (byte) 0x61: OUT(H()); t(8); break;
        case (byte) 0x62: HL(sbc16(HL(), HL())); t(11); break;
        case (byte) 0x63: poke16(fetch16(), HL()); t(16); break;
        case (byte) 0x67: RRD(); t(14); break;

        case (byte) 0x68: L(IN()); t(8); break;
        case (byte) 0x69: OUT(L()); t(8); break;
        case (byte) 0x6A: HL(adc16(HL(), HL())); t(11); break;
        case (byte) 0x6B: HL(peek16(fetch16())); t(16); break;
        
        case (byte) 0x6F: RLD(); t(14); break;

        case (byte) 0x72: HL(sbc16(HL(), SP())); t(11); break;
        case (byte) 0x73: poke16(fetch16(), SP()); t(16); break;

        case (byte) 0x78: A(IN()); t(8); break;
        case (byte) 0x79: OUT(A()); t(8); break;
        case (byte) 0x7A: HL(adc16(HL(), SP())); t(11); break;
        case (byte) 0x7B: SP(peek16(fetch16())); t(16); break;

        case (byte) 0xA0: LDI(); break;
        case (byte) 0xA1: CPI(); break;
        case (byte) 0xA2: INI(); t(12); break;
        case (byte) 0xA3: OUTI(); t(12); break;
        case (byte) 0xA8: LDD(); break;
        case (byte) 0xA9: CPD(); break;
        case (byte) 0xAA: IND(); t(12); break;
        case (byte) 0xAB: OUTD(); t(12); break;
        case (byte) 0xB0: LDIR(); break;
        case (byte) 0xB1: CPIR(); break;
        case (byte) 0xB2: if (INIR()) t(17); else t(12); break;
        case (byte) 0xB3: if (OTIR()) t(17); else t(12); break;
        case (byte) 0xB8: LDDR(); break;
        case (byte) 0xB9: CPDR(); break;
        case (byte) 0xBA: if (INDR()) t(17); else t(12); break;
        case (byte) 0xBB: if (OTDR()) t(17); else t(12); break;
        
        default: // System.err.println("ED Opcode unknown: " + Integer.toHexString(opcode));
        }
    }
    
    private void processFDOpcode() {
        incR();

        byte opcode = fetch();

        switch (opcode) {
        case (byte) 0x09: IY(add16(IY(), BC())); t(11); break;

        case (byte) 0x19: IY(add16(IY(), DE())); t(11); break;
        case (byte) 0x21: IY(fetch16()); t(10); break;
        case (byte) 0x22: poke16(fetch16(), IY()); t(16); break;
        case (byte) 0x23: IY(inc0(IY())); t(6); break;
        case (byte) 0x24: IYh(inc(IYh())); t(4); break;
        case (byte) 0x25: IYh(dec(IYh())); t(4); break;
        case (byte) 0x26: IYh(fetch()); t(7); break;

        case (byte) 0x29: IY(add16(IY(), IY())); t(11); break;
        case (byte) 0x2A: IY(peek16(fetch16())); t(16); break;
        case (byte) 0x2B: IY(dec0(IY())); t(6); break;
        case (byte) 0x2C: IYl(inc(IYl())); t(4); break;
        case (byte) 0x2D: IYl(dec(IYl())); t(4); break;
        case (byte) 0x2E: IYl(fetch()); t(7); break;

        case (byte) 0x34: tmp=add0(IY(), fetch()); poke(tmp, inc(peek(tmp))); t(19); break;
        case (byte) 0x35: tmp=add0(IY(), fetch()); poke(tmp, dec(peek(tmp))); t(19); break;
        case (byte) 0x36: poke(add0(IY(), fetch()), fetch()); t(15); break;
        case (byte) 0x39: IY(add16(IY(), SP())); t(11); break;
        
        case (byte) 0x44: B(IYh()); t(4); break;
        case (byte) 0x45: B(IYl()); t(4); break;
        case (byte) 0x46: B(peek(add0(IY(), fetch()))); t(15); break;
        
        case (byte) 0x4C: C(IYh()); t(4); break;
        case (byte) 0x4D: C(IYl()); t(4); break;
        case (byte) 0x4E: C(peek(add0(IY(), fetch()))); t(15); break;
        
        case (byte) 0x54: D(IYh()); t(4); break;
        case (byte) 0x55: D(IYl()); t(4); break;
        case (byte) 0x56: D(peek(add0(IY(), fetch()))); t(15); break;

        case (byte) 0x5C: E(IYh()); t(4); break;
        case (byte) 0x5D: E(IYl()); t(4); break;
        case (byte) 0x5E: E(peek(add0(IY(), fetch()))); t(15); break;
        
        case (byte) 0x60: IYh(B()); t(4); break;
        case (byte) 0x61: IYh(C()); t(4); break;
        case (byte) 0x62: IYh(D()); t(4); break;
        case (byte) 0x63: IYh(E()); t(4); break;
        case (byte) 0x64: IYh(IYh()); t(4); break;
        case (byte) 0x65: IYh(IYl()); t(4); break;
        case (byte) 0x66: H(peek(add0(IY(), fetch()))); t(15); break;
        case (byte) 0x67: IYh(A()); t(4); break;

        case (byte) 0x68: IYl(B()); t(4); break;
        case (byte) 0x69: IYl(C()); t(4); break;
        case (byte) 0x6A: IYl(D()); t(4); break;
        case (byte) 0x6B: IYl(E()); t(4); break;
        case (byte) 0x6C: IYl(IYh()); t(4); break;
        case (byte) 0x6D: IYl(IYl()); t(4); break;
        case (byte) 0x6E: L(peek(add0(IY(), fetch()))); t(15); break;
        case (byte) 0x6F: IYl(A()); t(4); break;
        
        case (byte) 0x70: poke(add0(IY(), fetch()), B()); t(15); break;
        case (byte) 0x71: poke(add0(IY(), fetch()), C()); t(15); break;
        case (byte) 0x72: poke(add0(IY(), fetch()), D()); t(15); break;
        case (byte) 0x73: poke(add0(IY(), fetch()), E()); t(15); break;
        case (byte) 0x74: poke(add0(IY(), fetch()), H()); t(15); break;
        case (byte) 0x75: poke(add0(IY(), fetch()), L()); t(15); break;
        case (byte) 0x77: poke(add0(IY(), fetch()), A()); t(15); break;

        case (byte) 0x7C: A(IYh()); t(4); break;
        case (byte) 0x7D: A(IYl()); t(4); break;
        case (byte) 0x7E: A(peek(add0(IY(), fetch()))); t(15); break;
        
        case (byte) 0x84: A(add(A(), IYh())); t(4); break; 
        case (byte) 0x85: A(add(A(), IYl())); t(4); break; 
        case (byte) 0x86: A(add(A(), peek(add0(IY(), fetch())))); t(15); break;

        case (byte) 0x8C: A(adc(A(), IYh())); t(4); break; 
        case (byte) 0x8D: A(adc(A(), IYl())); t(4); break; 
        case (byte) 0x8E: A(adc(A(), peek(add0(IY(), fetch())))); t(15); break;
        
        case (byte) 0x94: A(sub(A(), IYh())); t(4); break; 
        case (byte) 0x95: A(sub(A(), IYl())); t(4); break; 
        case (byte) 0x96: A(sub(A(), peek(add0(IY(), fetch())))); t(15); break;

        case (byte) 0x9C: A(sbc(A(), IYh())); t(4); break; 
        case (byte) 0x9D: A(sbc(A(), IYl())); t(4); break; 
        case (byte) 0x9E: A(sbc(A(), peek(add0(IY(), fetch())))); t(15); break;
        
        case (byte) 0xA4: A(and(A(), IYh())); t(4); break; 
        case (byte) 0xA5: A(and(A(), IYl())); t(4); break; 
        case (byte) 0xA6: A(and(A(), peek(add0(IY(), fetch())))); t(15); break;
        
        case (byte) 0xAC: A(xor(A(), IYh())); t(4); break; 
        case (byte) 0xAD: A(xor(A(), IYl())); t(4); break; 
        case (byte) 0xAE: A(xor(A(), peek(add0(IY(), fetch())))); t(15); break;
        
        case (byte) 0xB4: A(or(A(), IYh())); t(4); break; 
        case (byte) 0xB5: A(or(A(), IYl())); t(4); break; 
        case (byte) 0xB6: A(or(A(), peek(add0(IY(), fetch())))); t(15); break;
        
        case (byte) 0xBC: cp(A(), IYh()); t(4); break;
        case (byte) 0xBD: cp(A(), IYl()); t(4); break;
        case (byte) 0xBE: cp(A(), peek(add0(IY(), fetch()))); t(15); break;
        
        case (byte) 0xCB: t(4); processFDCBOpcode(); break;
        
        case (byte) 0xE1: IY(pop16()); t(11); break;
        case (byte) 0xE3: tmp=peek16(SP()); poke16(SP(), IY()); IY(tmp); t(19); break;
        case (byte) 0xE5: push16(IY()); t(11); break;
        
        case (byte) 0xE9: jump(IY()); t(4); break;

        case (byte) 0xF9: SP(IY()); t(6); break;
        default: // System.err.println("FD Opcode unknown: " + Integer.toHexString(opcode));
        }
    }
    
    private void processFDCBOpcode() {
        byte d = fetch();

        byte opcode = fetch();
        
        switch (opcode) {
        case (byte) 0x06: tmp=add0(IY(), d); poke(tmp, rlc(peek(tmp), false)); t(15); break;
        case (byte) 0x0E: tmp=add0(IY(), d); poke(tmp, rrc(peek(tmp), false)); t(15); break;
        case (byte) 0x16: tmp=add0(IY(), d); poke(tmp, rl(peek(tmp), false)); t(15); break;
        case (byte) 0x1E: tmp=add0(IY(), d); poke(tmp, rr(peek(tmp), false)); t(15); break;
        case (byte) 0x26: tmp=add0(IY(), d); poke(tmp, sla(peek(tmp))); t(15); break;
        case (byte) 0x2E: tmp=add0(IY(), d); poke(tmp, sra(peek(tmp))); t(15); break;
        case (byte) 0x36: tmp=add0(IY(), d); poke(tmp, sll(peek(tmp))); t(15); break;
        case (byte) 0x3E: tmp=add0(IY(), d); poke(tmp, srl(peek(tmp))); t(15); break;
        
        case (byte) 0x46: tmp=add0(IY(), d); biti(0, tmp); t(12); break;
        case (byte) 0x4E: tmp=add0(IY(), d); biti(1, tmp); t(12); break;
        case (byte) 0x56: tmp=add0(IY(), d); biti(2, tmp); t(12); break;
        case (byte) 0x5E: tmp=add0(IY(), d); biti(3, tmp); t(12); break;
        case (byte) 0x66: tmp=add0(IY(), d); biti(4, tmp); t(12); break;
        case (byte) 0x6E: tmp=add0(IY(), d); biti(5, tmp); t(12); break;
        case (byte) 0x76: tmp=add0(IY(), d); biti(6, tmp); t(12); break;
        case (byte) 0x7E: tmp=add0(IY(), d); biti(7, tmp); t(12); break;
        
        case (byte) 0x86: tmp=add0(IY(), d); poke(tmp, res(0, peek(tmp))); t(15); break;
        case (byte) 0x8E: tmp=add0(IY(), d); poke(tmp, res(1, peek(tmp))); t(15); break;
        case (byte) 0x96: tmp=add0(IY(), d); poke(tmp, res(2, peek(tmp))); t(15); break;
        case (byte) 0x9E: tmp=add0(IY(), d); poke(tmp, res(3, peek(tmp))); t(15); break;
        case (byte) 0xA6: tmp=add0(IY(), d); poke(tmp, res(4, peek(tmp))); t(15); break;
        case (byte) 0xAE: tmp=add0(IY(), d); poke(tmp, res(5, peek(tmp))); t(15); break;
        case (byte) 0xB6: tmp=add0(IY(), d); poke(tmp, res(6, peek(tmp))); t(15); break;
        case (byte) 0xBE: tmp=add0(IY(), d); poke(tmp, res(7, peek(tmp))); t(15); break;

        case (byte) 0xC6: tmp=add0(IY(), d); poke(tmp, set(0, peek(tmp))); t(15); break;
        case (byte) 0xCE: tmp=add0(IY(), d); poke(tmp, set(1, peek(tmp))); t(15); break;
        case (byte) 0xD6: tmp=add0(IY(), d); poke(tmp, set(2, peek(tmp))); t(15); break;
        case (byte) 0xDE: tmp=add0(IY(), d); poke(tmp, set(3, peek(tmp))); t(15); break;
        case (byte) 0xE6: tmp=add0(IY(), d); poke(tmp, set(4, peek(tmp))); t(15); break;
        case (byte) 0xEE: tmp=add0(IY(), d); poke(tmp, set(5, peek(tmp))); t(15); break;
        case (byte) 0xF6: tmp=add0(IY(), d); poke(tmp, set(6, peek(tmp))); t(15); break;
        case (byte) 0xFE: tmp=add0(IY(), d); poke(tmp, set(7, peek(tmp))); t(15); break;
        default: // System.err.println("FDCB Opcode unknown: " + Integer.toHexString(opcode));
        }
    }

    
    /* ===========================
     * Register and flags access.
     * Three groups of methods are available:
     *  - getters - gets the value of a registry
     *  - non-masking setters - sets the value without masking. 
     *  - masking setters - sets the value with masking. 
     * ===========================*/
    
    // Getters
    byte A() {return A;}
    byte F() {return F;}
    byte B() {return hi(BC);}
    byte C() {return lo(BC);}
    byte D() {return hi(DE);}
    byte E() {return lo(DE);}
    byte H() {return hi(HL);}
    byte L() {return lo(HL);}
    byte IXh() {return hi(IX);}
    byte IXl() {return lo(IX);}
    byte IYh() {return hi(IY);}
    byte IYl() {return lo(IY);}
    byte I() {return I;}
    byte R() {return R;}
    
    short BC() {return BC;}
    short DE() {return DE;}
    short HL() {return HL;}
    short IX() {return IX;}
    short IY() {return IY;}
    short PC() {return PC;}
    short SP() {return SP;}
    short AF() {return word(A, F);}
    
    boolean IFF1() {return IFF1;}
    boolean IFF2() {return IFF2;}
    
    boolean CF() {return (F & 0x01) != 0;} 
    boolean NF() {return (F & 0x02) != 0;} 
    boolean PF() {return (F & 0x04) != 0;} 
    boolean XF() {return (F & 0x08) != 0;} 
    boolean HF() {return (F & 0x10) != 0;} 
    boolean YF() {return (F & 0x20) != 0;} 
    boolean ZF() {return (F & 0x40) != 0;} 
    boolean SF() {return (F & 0x80) != 0;} 

    static boolean CF(byte f) {return (f & 0x01) != 0;}
    static boolean NF(byte f) {return (f & 0x02) != 0;}
    static boolean PF(byte f) {return (f & 0x04) != 0;}
    static boolean XF(byte f) {return (f & 0x08) != 0;}
    static boolean HF(byte f) {return (f & 0x10) != 0;}
    static boolean YF(byte f) {return (f & 0x20) != 0;}
    static boolean ZF(byte f) {return (f & 0x40) != 0;}
    static boolean SF(byte f) {return (f & 0x80) != 0;}
    
    // Non-masking setters
    void A(byte value) {A = value;}
    void F(byte value) {F = value;}
    void B(byte value) {BC = word(value, lo(BC));}
    void C(byte value) {BC = word(hi(BC), value);}
    void D(byte value) {DE = word(value, lo(DE));}
    void E(byte value) {DE = word(hi(DE), value);}
    void H(byte value) {HL = word(value, lo(HL));}
    void L(byte value) {HL = word(hi(HL), value);}
    void IXh(byte value) {IX = word(value, lo(IX));}
    void IXl(byte value) {IX = word(hi(IX), value);}
    void IYh(byte value) {IY = word(value, lo(IY));}
    void IYl(byte value) {IY = word(hi(IY), value);}
    void I(byte value) {I = value;}
    void R(byte value) {R = value;}
    
    void BC(short value) {BC = value;}
    void DE(short value) {DE = value;}
    void HL(short value) {HL = value;}
    void IX(short value) {IX = value;}
    void IY(short value) {IY = value;}
    void PC(short value) {PC = value;}
    void SP(short value) {SP = value;}
    void AF(short value) {A = (byte) (value >>> 8); F = (byte) value;}
    
    void IFF1(boolean value) {IFF1 = value;}
    void IFF2(boolean value) {IFF2 = value;}
    
    void CF(boolean value) {if (value) F |= 0x01; else F &= 0xFE;} 
    void NF(boolean value) {if (value) F |= 0x02; else F &= 0xFD;} 
    void PF(boolean value) {if (value) F |= 0x04; else F &= 0xFB;} 
    void XF(boolean value) {if (value) F |= 0x08; else F &= 0xF7;} 
    void HF(boolean value) {if (value) F |= 0x10; else F &= 0xEF;} 
    void YF(boolean value) {if (value) F |= 0x20; else F &= 0xDF;} 
    void ZF(boolean value) {if (value) F |= 0x40; else F &= 0xBF;} 
    void SF(boolean value) {if (value) F |= 0x80; else F &= 0x7F;} 
    
    static byte CF(byte f, boolean value) {if (value) return (byte) (f | 0x01); else return (byte) (f & 0xFE);} 
    static byte NF(byte f, boolean value) {if (value) return (byte) (f | 0x02); else return (byte) (f & 0xFD);}
    static byte PF(byte f, boolean value) {if (value) return (byte) (f | 0x04); else return (byte) (f & 0xFB);}
    static byte XF(byte f, boolean value) {if (value) return (byte) (f | 0x08); else return (byte) (f & 0xF7);}
    static byte HF(byte f, boolean value) {if (value) return (byte) (f | 0x10); else return (byte) (f & 0xEF);}
    static byte YF(byte f, boolean value) {if (value) return (byte) (f | 0x20); else return (byte) (f & 0xDF);}
    static byte ZF(byte f, boolean value) {if (value) return (byte) (f | 0x40); else return (byte) (f & 0xBF);}
    static byte SF(byte f, boolean value) {if (value) return (byte) (f | 0x80); else return (byte) (f & 0x7F);}
    
    // Helper methods for flags updateulating
    private static boolean[] mapPF = new boolean [256];
    static {
        mapPF [0] = true;
        mapPF [1] = false;

        int dst = 2; 
        while (dst < 256) {
            int src = 0;
            int n = dst;
            while (n > 0) {
                mapPF [dst++] = !mapPF [src++];
                n--;
            }
        }
    }
    
    void SF(int result) {SF((result & 0x80) != 0);}
    void SF16(int result) {SF((result & 0x8000) != 0);}
    void ZF(int result) {ZF((result & 0xFF) == 0);}
    void ZF16(int result) {ZF((result & 0xFFFF) == 0);}
    void XF(int result) {XF((result & 0x20) != 0);}
    void XF16(int result) {XF((result & 0x2000) != 0);}
    void HFadd(int a, int b) {
        int result = ((a & 0x0F) + (b & 0x0F)) & 0x10;
        HF(result != 0);
    }
    void HFsub(int a, int b) {
        int result = ((a & 0x0F) - (b & 0x0F)) & 0x10;
        HF(result != 0);
    }
    
    void HFadd16(int a, int b) {
        int result = (((a >>> 8) & 0x0F) + ((b >>> 8) & 0x0F)) & 0x10;
        HF(result != 0);
    }
    
    void HFsub16(int a, int b) {
        int result = (((a >>> 8) & 0x0F) - ((b >>> 8) & 0x0F)) & 0x10;
        HF(result != 0);
    }
    
    void YF(int result) {YF((result & 0x08) != 0);}
    void YF16(int result) {YF((result & 0x0800) != 0);}
    void PF(int result) {PF(mapPF [result & 0xFF]);}
    void VFadd(int a, int b) {
        PF((((a ^ b) & 0x80) == 0) ? ((((a + b) & 0x80) == 0) ? false : true) : false);
    }
    
    void VFsub(int a, int b) {
        PF((((a ^ b) & 0x80) == 0) ? false : ((((a - b) & 0x80) == 0) ? false : true));
    }
    
    void VFadd16(int a, int b) {
        PF((((a ^ b) & 0x8000) == 0) ? ((((a + b) & 0x8000) == 0) ? false : true) : false);
    }
    
    void VFsub16(int a, int b) {
        PF((((a ^ b) & 0x8000) == 0) ? false : ((((a - b) & 0x8000) == 0) ? false : true));
    }
    
    void CF(int result) {CF((result & 0x100) != 0);}
    void CF16(int result) {CF((result & 0x10000) != 0);}


    static boolean sSF(int result) {return (result & 0x80) != 0;}
    static boolean sSF16(int result) {return (result & 0x8000) != 0;}
    static boolean sZF(int result) {return (result & 0xFF) == 0;}
    static boolean sZF16(int result) {return (result & 0xFFFF) == 0;}
    static boolean sXF(int result) {return (result & 0x20) != 0;}
    static boolean sXF16(int result) {return (result & 0x2000) != 0;}
    static boolean sHFadd(int a, int b) {
        int result = ((a & 0x0F) + (b & 0x0F)) & 0x10;
        return result != 0;
    }
    static boolean sHFsub(int a, int b) {
        int result = ((a & 0x0F) - (b & 0x0F)) & 0x10;
        return result != 0;
    }
    
    static boolean sHFadd16(int a, int b) {
        int result = (((a >>> 8) & 0x0F) + ((b >>> 8) & 0x0F)) & 0x10;
        return result != 0;
    }
    
    static boolean sHFsub16(int a, int b) {
        int result = (((a >>> 8) & 0x0F) - ((b >>> 8) & 0x0F)) & 0x10;
        return result != 0;
    }
    
    static boolean sYF(int result) {return (result & 0x08) != 0;}
    static boolean sYF16(int result) {return (result & 0x0800) != 0;}
    static boolean sPF(int result) {return mapPF [result & 0xFF];}
    static boolean sVFadd(int a, int b) {
        return (((a ^ b) & 0x80) == 0) ? ((((a + b) & 0x80) == 0) ? false : true) : false;
    }
    
    static boolean sVFsub(int a, int b) {
        return (((a ^ b) & 0x80) == 0) ? false : ((((a - b) & 0x80) == 0) ? false : true);
    }
    
    static boolean sVFadd16(int a, int b) {
        return (((a ^ b) & 0x8000) == 0) ? ((((a + b) & 0x8000) == 0) ? false : true) : false;
    }
    
    static boolean sVFsub16(int a, int b) {
        return (((a ^ b) & 0x8000) == 0) ? false : ((((a - b) & 0x8000) == 0) ? false : true);
    }
    
    static boolean sCF(int result) {return (result & 0x100) != 0;}
    static boolean sCF16(int result) {return (result & 0x10000) != 0;}

    // Masking setters
    // Helpers
    void t(long ticks) {
        this.cycles += ticks;
    }
    
    byte fetch() {
        short PC = PC();
        byte value = peek(PC);
        PC(inc0(PC()));
        return value;
    }

    short fetch16() {
        short PC = PC();
        short value = peek16(PC);
        PC(add0(PC, (short) 2));
        return value;
    }
    
    byte peek(short address) {return context.peek(address);}
    short peek16(short address) {return (short) 
        ((context.peek(address) & 0xFF) 
        | context.peek((short) (address+1)) << 8);}
    void poke(short address, byte value) {context.poke(address, value);}
    void poke16(short address, short value) {
        context.poke(address, (byte) value); 
        context.poke((short) (address+1), (byte) (value >>> 8));
    }
    
    void push(byte value) {
        SP((short) (SP() - 1));
        poke(SP(), value);
    }
    
    void push16(short value) {
        SP((short) (SP() - 2));
        poke16(SP(), value);
    }

    byte pop() {
        byte result = peek(SP());
        SP((short) (SP() + 1));
        return result;
    }

    short pop16() {
        short result = peek16(SP());
        SP((short) (SP() + 2));
        return result;
    }
    
    private void jump(short address) {PC(address);}
    private void jumpe(byte offset) {jump((short) (PC() + offset));}
    private boolean jumpc(short address, boolean condition) {
        if (condition) jump(address); 
        return condition;
    }
    private boolean jumpec(byte offset, boolean condition) {
        if (condition) jump((short) (PC() + offset));
        return condition;
    }

    private void call(short address) {
        push16(PC());
        PC(address);
    }
    
    private boolean callc(short address, boolean condition) {
        if (condition) {
            call(address);
        }
        return condition;
    }
    
    private void ret() {
        PC(pop16());
    }
    
    private void reti() {
        ret();
    }
    
    private void retn() {
        ret();
        IFF1(IFF2());
    }
    
    private boolean retc(boolean condition) {
        if (condition) {
            ret();
        }
        return condition;
    }
    
    private void rst(byte address) {
        call((short) address);
    }
    
    void LD_A_I() {
        byte value = I();
        A(value);
                
        SF(value);
        ZF(value);
        YF(value);
        HF(false);
        XF(value);
        PF(IFF2());
        NF(false);
    }
    
    void LD_A_R() {
        byte value = R();
        A(value);
                
        SF(value);
        ZF(value);
        YF(value);
        HF(false);
        XF(value);
        PF(IFF2());
        NF(false);
    }

    void EXX() {
        tmp = BC; BC = BC_BIS; BC_BIS = tmp;
        tmp = DE; DE = DE_BIS; DE_BIS = tmp;
        tmp = HL; HL = HL_BIS; HL_BIS = tmp;
    }   
    
    void LDI() {
        byte value = peek(HL());
        poke(DE(), value);
        
        DE((short) (DE()+1));
        HL((short) (HL()+1));
        BC((short) (BC()-1));
        
        HF(false);
        PF(BC() != 0);
        NF(false);
        int n = A() + value;
        YF((n & 0x02) != 0);
        XF((n & 0x08) != 0);
        
        t(12);
    }
    
    void LDIR() {
        LDI();
        
        if (BC() != 0) {
            jumpe((byte) -2);
            t(5);           
        }
    }

    void LDD() {
        byte value = peek(HL());
        poke(DE(), value);
        DE(dec0(DE()));
        HL(dec0(HL()));
        BC(dec0(BC()));
        HF(false);
        PF(BC() != 0);
        NF(false);
        int n = A() + value;
        YF((n & 0x02) != 0);
        XF((n & 0x08) != 0);
        t(12);
    }
    
    void LDDR() {
        LDD();
        if (BC() != 0) {
            jumpe((byte) -2);
            t(5);
        }
    }
    
    void CPI() {
        int lValue = A();
        int rValue = peek(HL());
        int result = lValue - rValue;
        HL(inc0(HL()));
        BC(dec0(BC()));
        SF(result);
        ZF(result);
        HFsub(lValue, rValue);
        PF(BC() != 0);
        NF(true);
        int n = result - (HF() ? 1 : 0);
        YF((n & 0x02) != 0);
        XF((n & 0x08) != 0);
        t(12);
    }
    
    void CPIR() {
        CPI();
        if ((BC() != 0) & (!ZF())) {
            jumpe((byte) -2);
            t(5); 
        } else {
            t(2);
        }
    }
    
    void CPD() {
        int lValue = A();
        int rValue = peek(HL());
        int result = lValue - rValue;
        HL(dec0(HL()));
        BC(dec0(BC()));
        SF(result);
        ZF(result);
        HFsub(lValue, rValue);
        PF(BC() != 0);
        NF(true);
        int n = result - (HF() ? 1 : 0);
        YF((n & 0x02) != 0);
        XF((n & 0x08) != 0);
        t(12);
    }
    
    void CPDR() {
        CPD();
        if ((BC() != 0) & (!ZF())) {
            jumpe((byte) -2);
            t(5);
        } else {
            t(2);
        }
    }
    
    private byte add(byte a, byte b) {
        int result = (a & 0xFF) + (b & 0xFF);
        SF(result);
        ZF(result);
        YF(result);
        HFadd(a, b);
        XF(result);
        VFadd(a, b);
        NF(false);
        CF(result);
        return (byte) result;
    }
    
    private byte adc(byte a, byte b) {
        int ia = a & 0xFF;
        int ib = (b & 0xFF) + (CF() ? 1 : 0);
        int result = ia + ib;
        SF(result);
        ZF(result);
        YF(result);
        HFadd(ia, ib);
        XF(result);
        VFadd(ia, ib);
        NF(false);
        CF(result);
        return (byte) result;
    }
    
    private byte sub(byte a, byte b) {
        int result = (a & 0xFF) - (b & 0xFF);
        SF(result);
        ZF(result);
        YF(result);
        HFsub(a, b);
        XF(result);
        VFsub(a, b);
        NF(true);
        CF(result);
        return (byte) result;
    }
    
    private byte sbc(byte a, byte b) {
        int ia = a & 0xFF;
        int ib = (b & 0xFF) + (CF() ? 1 : 0);
        int result = ia - ib;
        SF(result);
        ZF(result);
        YF(result);
        HFsub(ia, ib);
        XF(result);
        VFsub(ia, ib);
        NF(true);
        CF(result);
        return (byte) result;
    }
    
    private byte inc(byte a) {
        int result = (a & 0xFF) + 1;
        SF(result);
        ZF(result);
        YF(result);
        HFadd(a, 1);
        XF(result);
        VFadd(a, 1);
        NF(false);
        return (byte) result;
    }
    
    private byte dec(byte a) {
        int result = (a & 0xFF) - 1;
        SF(result);
        ZF(result);
        YF(result);
        HFsub(a, 1);
        XF(result);
        VFsub(a, 1);
        NF(true);
        return (byte) result;
    }
    
    private byte and(byte a, byte b) {
        int result = a & b;
        SF(result);
        ZF(result);
        YF(result);
        HF(true);
        XF(result);
        PF(result);
        NF(false);
        CF(false);
        return (byte) result;
    }
    
    private byte or(byte a, byte b) {
        int result = a | b;
        SF(result);
        ZF(result);
        YF(result);
        HF(false);
        XF(result);
        PF(result);
        NF(false);
        CF(false);
        return (byte) result;
    }
    
    private byte xor(byte a, byte b) {
        int result = a ^ b;
        SF(result);
        ZF(result);
        YF(result);
        HF(false);
        XF(result);
        PF(result);
        NF(false);
        CF(false);
        return (byte) result;
    }
    
    private void cp(byte a, byte b) {
        int result = (a & 0xFF) - (b & 0xFF);
        SF(result);
        ZF(result);
        YF(b);
        HFsub(a, b);
        XF(b);
        VFsub(a, b);
        NF(true);
        CF(result);
    }
    
    void DAA() {
        int a = A() & 0xFF;
        int tmp2 = 0;
        int tmp3 = (CF() ? 1 : 0);

        int tmp = tmp3;
        if (HF() || ((a & 0x0f) > 0x09)) {
            tmp2 |= 0x06;
        }

        if ((tmp3 == 1) || (a > 0x9f) || ((a > 0x8f) && ((a & 0x0f) > 0x09))) {
            tmp2 |= 0x60;
            tmp = 1;
        }

        if ( a > 0x99 ) {
            tmp = 1;
        }   

        if (NF()) {
            A(sub((byte) a, (byte) tmp2));
        } else {    
            A(add((byte) a, (byte) tmp2));
        }

        F((byte) ((F() & 0xFE) | tmp));
        if (mapPF[A() & 0xFF]) {
            F((byte) ((F() & 0xFB) | 4));
        } else {
            F((byte) (F() & 0xFB));
        }
    }

    void CPL() {
        int result = A() ^ 0xFF;
        A((byte) result);
        YF(result);
        HF(true);
        XF(result);
        NF(true);
    }
    
    void NEG() {
        int value = A() & 0xFF;
        int result = -value;
        A((byte) result);
        SF(result);
        ZF(result);
        YF(result);
        HFsub(0, value);
        XF(result);
        PF(value == 0x80);
        NF(true);
        CF(value != 0);
    }
    
    void CCF() {
        int value = A();
        HF(CF());
        CF(!CF());
        NF(false);
        YF(value);
        XF(value);              
    }
    
    void SCF() {
        int value = A();
        CF(true);
        HF(false);
        NF(false);
        YF(value);
        XF(value);              
    }
    
    void DI() {
        IFF1(false);
        IFF2(false);
    }
    
    void EI() {
        IFF1(true);
        IFF2(true);
        // TODO:Do not accept IRQ immediately after EI.
        //acceptIRQ = false;
    }
    
    short add16(short lValue, short rValue) {
        int ilValue = lValue & 0xFFFF;
        int irValue = rValue & 0xFFFF;
        int result = ilValue + irValue;
        YF16(result);
        HFadd16(ilValue, irValue);
        XF16(result);
        NF(false);
        CF16(result);
        return (short) result;
    }   
    
    short adc16(short lValue, short rValue) {
        int ilValue = (lValue & 0xFFFF);
        int irValue = (rValue & 0xFFFF) + (CF() ? 1 : 0);
        int result = ilValue + irValue;
        SF16(result);
        ZF16(result);
        YF16(result);
        HFadd16(ilValue, irValue);
        XF16(result);
        VFadd16(ilValue, irValue);
        NF(false);
        CF16(result);
        return (short) result;
    }   
    
    short sbc16(short lValue, short rValue) {
        int ilValue = (lValue & 0xFFFF);
        int irValue = (rValue & 0xFFFF) + (CF() ? 1 : 0);
        int result = ilValue - irValue;
        SF16(result);
        ZF16(result);
        YF16(result);
        HFsub16(ilValue, irValue);
        XF16(result);
        VFsub16(ilValue, irValue);
        NF(false);
        CF16(result);
        return (short) result;
    }   
    
    private void updateRotateShiftFlags(int result, boolean fast) {
        YF(result);
        HF(false);
        XF(result);
        NF(false);
        CF(result);
        if (!fast) {
            SF(result);
            ZF(result);
            PF(result);
        }
    }

    private byte sla(byte value) {
        int result = value << 1;
        updateRotateShiftFlags(result, false);
        return (byte) result;
    }
    
    private byte sll(byte value) {
        int result = (value << 1) | 0x01;
        updateRotateShiftFlags(result, false);
        return (byte) result;
    }
    
    private byte sra(byte value) {
        int result = ((value & 0xFF) >>> 1) | (value & 0x80) | ((value & 0x01) << 8);
        updateRotateShiftFlags(result, false);
        return (byte) result;
    }
    
    private byte srl(byte value) {
        int result = ((value & 0xFF) >>> 1) | ((value & 0x01) << 8);
        updateRotateShiftFlags(result, false);
        return (byte) result;
    }
    
    private byte rl(byte value, boolean fast) {
        int result = (value << 1) | (CF() ? 1 : 0);
        updateRotateShiftFlags(result, fast);
        return (byte) result;
    }
    
    private byte rr(byte value, boolean fast) {
        int result = ((value & 0xFF) >>> 1) | (CF() ? 0x80 : 0) | ((value & 0x01) << 8);
        updateRotateShiftFlags(result, fast);
        return (byte) result;
    }
    
    private byte rlc(byte value, boolean fast) {
        int result = (value << 1) | (((value & 0x80) == 0) ? 0 : 1);
        updateRotateShiftFlags(result, fast);
        return (byte) result;
    }
    
    private byte rrc(byte value, boolean fast) {
        int result = ((value & 0xFF) >>> 1) | ((value & 0x01) << 8) | ((value & 0x01) << 7);
        updateRotateShiftFlags(result, fast);
        return (byte) result;
    }

    private void bit(int bit, byte value) {
        boolean bitSet = (value & (0x01 << bit)) != 0;
        SF((bit == 7) & bitSet);
        ZF(!bitSet);
        YF((bit == 5) & bitSet);
        HF(true);
        XF((bit == 3) & bitSet);
        PF(!bitSet);
        NF(false);
    }
    
    private void biti(int bit, short address) {
        byte value = peek(address); 
        bit(bit, value);
        byte hiAddress = hi(address);
        YF((bit == 5) & ((hiAddress & 0x20) != 0));
        XF((bit == 3) & ((hiAddress & 0x08) != 0));
    }
    
    private byte set(int bit, byte value) {return (byte) (value | (0x01 << bit));}
    private byte res(int bit, byte value) {return (byte) (value & ((0x01 << bit) ^ 0xFF));}
    
    void RLD() {
        byte a = A();
        byte ihl = peek(HL());
        byte result = (byte) ((a & 0xF0) | (ihl >>> 4));
        poke(HL, (byte) ((ihl << 4) | (a & 0x0F)));
        A(result);
        SF(result);
        ZF(result);
        HF(false);
        PF(result);
        NF(false);
    }
    
    void RRD() {
        byte a = A();
        byte ihl = peek(HL());
        byte result = (byte) ((a & 0xF0) | (ihl & 0x0F));
        poke(HL(), (byte) ((a << 4) | (ihl >>> 4)));
        A(result);
        SF(result);
        ZF(result);
        HF(false);
        PF(result);
        NF(false);
    }
    
    boolean DJNZ(byte d) {
        B(dec0(B()));
        if (B() != 0) {
            jumpe(d);
            return true;
        } else {
            return false;
        }
    }
    
    void INA(byte loPort) {
        byte result = context.in(word(A(), loPort));
        A(result);
    }
    
    byte IN() {
        byte result = context.in(BC());
        SF(result);
        ZF(result);
        YF(result);
        HF(false);
        XF(result);
        PF(result);
        NF(false);
        return (byte) result;
    }
    
    void INI() {
        byte result = context.in(BC());
        poke(HL(), result);
        HL(inc0(HL()));
        int bResult = (B() & 0xFF) - 1;
        int cResult = (inc0(C()) & 0xFF) + (result & 0xFF);
        B((byte) bResult);
        SF(bResult);
        ZF(bResult);
        YF(bResult);
        XF(bResult);
        CF(cResult > 255);
        HF(cResult > 255);
        NF((result & 0x80) != 0);
    }
    
    boolean INIR() {
        INI();
        if(B() != 0) {
            jumpe((byte) -2);
            return true;
        } else {
            return false;
        }
    }
    
    void IND() {
        byte result = context.in(BC());
        poke(HL(), result);
        HL(dec0(HL()));
        B(dec0(B()));
        ZF(B());
        NF(true);
    }
    
    boolean INDR() {
        IND();
        if(B() != 0) {
            jumpe((byte) -2);
            return true;
        } else {
            return false;
        }
    }
    
    void OUTA(byte loPort) {
        context.out(word(A(), loPort), A());
    }
    
    void OUT(byte value) {
        context.out(BC(), value);
    }
    
    void OUTI() {
        byte value = peek(HL());
        B(dec0(B()));
        context.out(BC(), value);
        HL(inc0(HL()));
        ZF(B());
        NF(true);
    }
    
    boolean OTIR() {
        OUTI();
        if(B() != 0) {
            jumpe((byte) -2);
            return true;
        } else {
            return false;
        }
    }
    
    void OUTD() {
        byte value = peek(HL());
        B(dec0(B()));
        context.out(BC(), value);
        HL(dec0(HL()));
        ZF(B());
        NF(true);
    }
    
    boolean OTDR() {
        OUTD();
        if(B() != 0) {
            jumpe((byte) -2);
            return true;
        } else {
            return false;
        }
    }
    
    private void leaveHALT() {
        //System.out.println("Leaving HALT");
        if(peek(PC()) == 0x76) PC(inc0(PC()));
    }
    
    private void incR() {
        byte r = R();
        R((byte) ((r & 0x80) | ((r + 1) & 0x7F)));
    }
    
    // Raw operations on byte and short types.
    private byte inc0(byte value) {return (byte) (value+1);}
    private byte dec0(byte value) {return (byte) (value-1);}
    //private byte add0(byte a, byte b) {return (byte) (a+b);}
    //private byte sub0(byte a, byte b) {return (byte) (a-b);}
    private short inc0(short value) {return (short) (value+1);}
    private short dec0(short value) {return (short) (value-1);}
    private short add0(short a, short b) {return (short) (a+b);}
    //private short sub0(short a, short b) {return (short) (a-b);}
    private short word(byte hi, byte lo) {return (short) ((hi << 8) | (lo & 0xFF));}
    private byte hi(short v) {return (byte) (v >>> 8);}
    private byte lo(short v) {return (byte) v;}

    // Abstract methods from Z80ImplBase superclass.
    public byte getA() {return A;}
    public byte getA2() {return A_BIS;}
    public byte getB() {return hi(BC);}
    public byte getB2() {return hi(BC_BIS);}
    public byte getC() {return lo(BC);}
    public byte getC2() {return lo(BC_BIS);}
    public byte getD() {return hi(DE);}
    public byte getD2() {return hi(DE_BIS);}
    public byte getE() {return lo(DE);}
    public byte getE2() {return lo(DE_BIS);}
    public byte getF() {return F;}
    public byte getF2() {return F_BIS;}
    public byte getH() {return hi(HL);}
    public byte getH2() {return hi(HL_BIS);}
    public byte getI() {return I;}
    public boolean getIFF1() {return IFF1;}
    public boolean getIFF2() {return IFF2;}
    public byte getIM() {return IM;}
    public short getIX() {return IX;}
    public short getIY() {return IY;}
    public byte getL() {return lo(HL);}
    public byte getL2() {return lo(HL_BIS);}
    public short getPC() {return PC;}
    public byte getR() {return R;}
    public short getSP() {return SP;}
    
    public void setA(byte value) {A = value;}
    public void setA2(byte value) {A_BIS = value;}
    public void setB(byte value) {BC = word(value, lo(BC));}
    public void setB2(byte value) {BC_BIS = word(value, lo(BC_BIS));}
    public void setC(byte value) {BC = word(hi(BC), value);}
    public void setC2(byte value) {BC_BIS = word(hi(BC_BIS), value);}
    public void setD(byte value) {DE = word(value, lo(DE));}
    public void setD2(byte value) {DE_BIS = word(value, lo(DE_BIS));}
    public void setE(byte value) {DE = word(hi(DE), value);}
    public void setE2(byte value) {DE_BIS = word(hi(DE_BIS), value);}
    public void setF(byte value) {F = value;}
    public void setF2(byte value) {F_BIS = value;}
    public void setH(byte value) {HL = word(value, lo(HL));}
    public void setH2(byte value) {HL_BIS = word(value, lo(HL_BIS));}
    public void setI(byte value) {I = value;}
    public void setIFF1(boolean value) {IFF1 = value;}
    public void setIFF2(boolean value) {IFF2 = value;}
    public void setIM(byte value) {IM = value;}
    public void setIX(short value) {IX = value;}
    public void setIY(short value) {IY = value;}
    public void setL(byte value) {HL = word(hi(HL), value);}
    public void setL2(byte value) {HL_BIS = word(hi(HL_BIS), value);}
    public void setPC(short value) {PC = value;}
    public void setR(byte value) {R = value;}
    public void setSP(short value) {SP = value;}
}
